﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Net.Mail;

using Scheduling.Models.Interfaces;
using Scheduling.Models.Extensions;
using Scheduling.Models.Repos.Database.Objects;

namespace Scheduling.Models.Helpers
{
    public static class UserHelper
    {
        /// <summary>
        /// Authenticates user credentials. 
        /// </summary>
        /// <param name="LoginName">The specified username. </param>
        /// <param name="Password">The specified password. </param>
        /// <returns>If authenticated, a user. Else null. </returns>
        public static User Authenticate(string LoginName, string Password)
        {
            var userLoader = IoC.Resolve<IUserLoader>();
            var user = userLoader.GetUser(LoginName);
            if (user == null) return null;
            if (user.Password != Password.Hash()) return null;
            return user;
        }

        /// <summary>
        /// Create A user from the strings and there will be no sqlinjections when user is added in database 
        /// </summary>
        /// <returns>A user created from the strings </returns>
        public static User CreateUserFromStrings(string ID, string GroupID, string DepartmentID, string FirstName, string LastName, string Password, string Email, string Phone)
        {
            var usergroups = IoC.Resolve<IUserGroupLoader>();
            var departments = IoC.Resolve<IDepartmentLoader>();
            User user = new User();
            user.Email = Email.Replace("'", "#39;");
            user.Department = departments.GetDepartment(int.Parse(DepartmentID));
            user.UserGroup = usergroups.GetUserGroup(int.Parse(GroupID));
            user.ID = ID.Replace("'", "#39;");
            user.FirstName = FirstName.Replace("'", "#39;");
            user.LastName = LastName.Replace("'", "#39;");
            user.Password = Password;
            user.Phone = Phone;
            return user;
        }

        /// <summary>
        /// Validates a User from the provided input. 
        /// </summary>
        /// <returns>A string containing the error message. The error message is null if no error was encountered. </returns>
        public static string ValidateUserInput(string ID, string GroupID, string DepartmentID, string FirstName, string LastName, string Password1, string Password2, string Email, string Phone)
        {
            var userLoader = IoC.Resolve<IUserLoader>();
            var departmentLoader = IoC.Resolve<IDepartmentLoader>();
            var groupLoader = IoC.Resolve<IUserGroupLoader>();

            if (groupLoader.GetUserGroup(int.Parse(GroupID)) == null)
                return "Group does not exist";

            if (departmentLoader.GetDepartment(int.Parse(DepartmentID)) == null)
                return "Department does not exist";

            if (ID == "" || FirstName == "" || LastName == "" || Email == "" || Password1 == "" ||Password2 == "")
            {
                return "All mandatory fields are not filled in";
            }
            if (Password1 != Password2)
            {
                return "Passwords do not match";
            }

            if (!Email.Match(".@"))
            {
                return "Email has incorect format";
            }
            if (Phone.Match(".[0-9]") && Phone == "")
            {
                return "Phone number has incorect format(Only digits are allowed)";
            }
            return null;
        }

        /// <summary>
        /// Checks if a user is authenticated. 
        /// </summary>
        public static bool IsUserAuthenticated()
        {
            return (!string.IsNullOrEmpty(SessionManager.UserID));
        }

        /// <summary>
        /// Returns the ID of the currently authenticated user. 
        /// </summary>
        /// <returns>User ID. </returns>
        public static string GetAuthenticatedUserID()
        {
            if (IsUserAuthenticated())
                return SessionManager.UserID;
            else
                return null;
        }

        /// <summary>
        /// Retrieves the authenticated user. 
        /// </summary>
        /// <returns>The authenticated user. </returns>
        public static User GetAuthenticatedUser()
        {
            var userLoader = IoC.Resolve<IUserLoader>();
            string userID = GetAuthenticatedUserID();
            if (userID == null) return null;
            return userLoader.GetUser(userID);
        }

        /// <summary>
        /// Loads a user into the session. 
        /// </summary>
        /// <param name="User">User to load into the current session. </param>
        public static void LoadIntoSession(User User)
        {
            var Session = HttpContext.Current.Session;
            Session["UserID"] = User.ID;
            Session["UserGroupID"] = User.UserGroup.ID;
            Session["UserGroupName"] = User.UserGroup.Name;
        }

        /// <summary>
        /// Unloads all user data from the session. 
        /// </summary>
        public static void UnloadUserFromSession()
        {
            var Session = HttpContext.Current.Session;
            Session["UserID"] = null;
            Session["UserGroupID"] = null;
        }

        /// <summary>
        /// Updates an authenticated users password.
        /// </summary>
        /// <param name="NewPassword">The new password for the user</param>
        public static void UpdateUserPassword(string NewPassword)
        {
            var userLoader = IoC.Resolve<IUserLoader>();
            var user = GetAuthenticatedUser();
            if (user != null)
            {
                user.Password = NewPassword.Hash();
                userLoader.EditUser(user);
            }
        }
        /// <summary>
        /// Updates a users password
        /// </summary>
        /// <param name="NewPassword">New password for user</param>
        /// <param name="UserID">ID of user</param>
        public static void UpdateUserPassword(string NewPassword, string UserID)
        {
            var userLoader = IoC.Resolve<IUserLoader>();

            if (userLoader.UserExists(UserID))
            {
                var user = userLoader.GetUser(UserID);
                user.Password = NewPassword.Hash();
                userLoader.EditUser(user);
                string query = "UPDATE Users SET ResetHash = null WHERE ID = '{0}'";
                var db = DBContext.Create();
                db.ExecuteCommand(query.SqlFormat(UserID));

            }
        }
        /// <summary>
        /// Generates a password reset token for a given user, identified by id.
        /// </summary>
        /// <param name="UserID">ID of the user requestion a password reset</param>
        /// <returns>The password reset token.</returns>
        public static string GeneratePasswordResetToken(string UserID)
        {
            string token = "";
            token += UserID;
            token += DateTime.Now.Year;
            token += DateTime.Now.Month;
            token += DateTime.Now.Day;
            token += DateTime.Now.TimeOfDay.ToString();
            string query = "UPDATE Users SET ResetHash = '{0}' WHERE ID = '{1}'";
            var db = DBContext.Create();
            db.ExecuteCommand(query.SqlFormat(token.Hash(), UserID));
            return token.Hash();
        }
        /// <summary>
        /// Sends a password reset email to a user
        /// </summary>
        /// <param name="inUserID">ID of user to recieve email</param>
        public static void SendResetPasswordEmail(string inUserID)
        {

            var userLoader = IoC.Resolve<IUserLoader>();

            if (userLoader.UserExists(inUserID))
            {
                MailMessage resetEmail = new MailMessage("adm@booking.kau.se", (userLoader.GetUser(inUserID).Email == null || !userLoader.GetUser(inUserID).Email.Contains('@')) ? "no@email.com" : userLoader.GetUser(inUserID).Email, "Password Reset Request", "");
                resetEmail.Body = "Hello!<br />You recently requested a password reset for your account at the KaU Booking system.<br />Click the following link to complete the password reset and select a new password.<br />";
                string resetToken = GeneratePasswordResetToken(inUserID);
                resetEmail.Body += "<a href=\"localhost:3092/user/resetpassword/?Token="+resetToken+"&UserID="+inUserID+"\">Password Reset</a>";
                resetEmail.IsBodyHtml = true;
                //SmtpClient sender = new SmtpClient("mail.student.kau.se");
                //sender.Send(resetEmail);
                MailHelper.DumpEmailToTextFile(resetEmail, "C:/resetEmail.txt");
            }
        }

        /// <summary>
        /// Verifies a reset password token
        /// </summary>
        /// <param name="Token">The token to be verified</param>
        /// <param name="UserID">ID for user requesting a reset</param>
        /// <returns></returns>
        public static bool VerifyResetToken(string Token, string UserID)
        {
            var db = DBContext.Create();
            var tokens = from t in db.Users
                           where t.ID == UserID && t.ResetHash == Token
                           select t;

            if (tokens.ToList().Count == 1)
                return true;
            else
                return false;
        }

        /// <summary>
        /// Checks if a certain user and email combination exists in the database. 
        /// </summary>
        /// <param name="Email">The email to check. </param>
        /// <param name="UserID">The ID to check. </param>
        /// <returns>True if user found, otherwise False. </returns>
        public static bool VerifyUserEmail(string Email, string UserID)
        {
            var db = DBContext.Create();
            var users = from u in db.Users
                         where u.ID == UserID && u.Email == Email
                         select u;

            if (users.ToList().Count == 1)
                return true;
            else
                return false;

        }
    }
}